package cn.zwx.mvcframework.webmvc.servlet;

import cn.zwx.mvcframework.annotation.ZwxController;
import cn.zwx.mvcframework.annotation.ZwxRequestMapping;
import cn.zwx.mvcframework.context.ZwxApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;

/**
 *
 * @author zhangwenxue
 * @date 2022/4/24 22:11
*/
public class ZwxDispatchServlet extends HttpServlet {

    private ZwxApplicationContext applicationContext;

    private List<ZwxHandlerMapping> handlerMappings = new ArrayList<>();

    /**
     * 保存HandlerMapping和对应的参数解析器映射关系
     */
    private Map<ZwxHandlerMapping,ZwxHandlerAdapter> handlerAdapters = new HashMap<>();


    private List<ZwxViewResolver> viewResolvers = new ArrayList<>();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        this.doPost(req, resp);
    }

    /**
     * 根据参数进行分发
     * 1.匹配handlerMapping
     * 2.解析参数
     * 3.反射调用invoker方法
     * 4.getWriter().write
     * @param req 请求参数
     * @param resp 返回参数
     * @throws IOException io异常
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 根据请求参数获取HandlerMapping对象
        try {
            this.doDispatch(req, resp);
        }catch (Exception e){
            this.processExceptionResult(req, resp, e);
        }
    }

    /**
     * 处理异常返回业务
     * @param req 请求参数
     * @param resp 响应参数
     * @param e 异常信息
     * @throws IOException IO异常
     */
    private void processExceptionResult(HttpServletRequest req, HttpServletResponse resp, Exception e) throws IOException {
        // 首先加载定义500的静态页面
        try {
            ZwxModelAndView modelAndView = new ZwxModelAndView("500");
            Map<String, String> stringStringMap = new HashMap<>(4);
            stringStringMap.put("detail",e.toString());
            stringStringMap.put("stackTrace", Arrays.toString(e.getStackTrace()));
            modelAndView.setModel(stringStringMap);
            this.processDispatchResult(resp,modelAndView);
        } catch (Exception exception) {
            resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
            exception.printStackTrace();
        }
    }


    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 1.根据request获取对于HandlerMapping
         ZwxHandlerMapping handlerMapping =  this.getHandler(req);
        // 如果没有匹配到就是404
        if (Objects.isNull(handlerMapping)){
            // 返回404.html
           this.processDispatchResult(resp,new ZwxModelAndView("404"));
           return;
        }
        // 2.根据一个HandlerMapping获得一个HandlerAdapter
        ZwxHandlerAdapter handlerAdapter = this.getHandlerAdapter(handlerMapping);
        if (Objects.isNull(handlerAdapter)){return;}
        // 3.解析某一个方法的形参和返回值之后，统一封装为ModelAndView对象
        ZwxModelAndView modelAndView = handlerAdapter.handler(req,resp,handlerMapping);
        // 4.根据modelAndView渲染浏览器
        this.processDispatchResult(resp,modelAndView);

    }

    private ZwxHandlerAdapter getHandlerAdapter(ZwxHandlerMapping handlerMapping ) {
        if (handlerAdapters.isEmpty()){
            return null;
        }
        return handlerAdapters.get(handlerMapping);
    }

    private void processDispatchResult(HttpServletResponse resp,ZwxModelAndView modelAndView) throws Exception {
        // 这里模拟返回json
        if (null == modelAndView.getViewName()){
            // 直接写出
            resp.getWriter().write(modelAndView.getModel().values().toString());
            return;
        }
        if (viewResolvers.isEmpty()){
            return;
        }
        // 这里是处理有视图的情况
        for (ZwxViewResolver resolver : viewResolvers) {
            ZwxView zwxView = resolver.resolveViewName(modelAndView.getViewName());
            //直接往浏览器输出
            zwxView.render(modelAndView.getModel(),resp);
            return;
        }
    }

    private ZwxHandlerMapping getHandler(HttpServletRequest req) {
        String requestUri = req.getRequestURI();
        String contextPath = req.getContextPath();
        // 获取我们内部url 多个/ 替换为一个/
        requestUri = requestUri.replace(contextPath, "").replaceAll("/+", "/");
        for (ZwxHandlerMapping handlerMapping : handlerMappings) {
            String pattern = handlerMapping.getPattern();
            if (pattern.matches(requestUri)){
                return handlerMapping;
            }
        }
        return null;
    }


    /**
    * 程序入口
    *<text>
    *     主要思路：
    *        第一步  由Servlet到Application构造
    *</text>
    *
    * @author zhangwenxue
    * @date 2022/4/24 22:14
   */
    @Override
    public void init(ServletConfig config) {
        // 1.初始化容器
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        applicationContext = new ZwxApplicationContext(contextConfigLocation);
        //==============MVC部分==============
        //初始化九大组件
        this.initStrategies(applicationContext);
        System.out.println("Zwx Spring framework is init.,");
    }

    /**
     * 初始化九大主键
     * @param context 请求参数
     */
    private void initStrategies(ZwxApplicationContext context) {
        //多文件上传的组件
        this.initMultipartResolver(context);
        //初始化本地语言环境
        this.initLocaleResolver(context);
        //初始化模板处理器
        this.initThemeResolver(context);
        //handlerMapping
        this.initHandlerMappings(context);
        //初始化参数适配器
        this.initHandlerAdapters(context);
        //初始化异常拦截器
        this.initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        this.initRequestToViewNameTranslator(context);
        //初始化视图转换器
        this.initViewResolvers(context);
        //FlashMap管理器
        this.initFlashMapManager(context);
    }

    private void initFlashMapManager(ZwxApplicationContext context) {
        System.out.println("FlashMap管理器。。。");
    }

    private void initViewResolvers(ZwxApplicationContext context) {
        Properties contextConfig = context.getConfig();
        // 获取到静态模板跟目录
        String templateRoot = contextConfig.getProperty("templateRoot");
        String templateRootPath = Objects.requireNonNull(this.getClass().getClassLoader().getResource(templateRoot)).getFile();
        File file = new File(templateRootPath);
        // 获取到所有的文件
        File[] files = file.listFiles();
        if (Objects.isNull(files) || files.length == 0){
            return;
        }
        // 把拿到模板信息保存起来
        for (File f : files) {
            String path = f.getParent();
            viewResolvers.add(new ZwxViewResolver(path));
        }
    }

    private void initRequestToViewNameTranslator(ZwxApplicationContext context) {
        System.out.println("初始化视图预处理器。。。");
    }

    private void initHandlerExceptionResolvers(ZwxApplicationContext context) {
        System.out.println("初始化异常拦截器。。。");
    }

    private void initHandlerAdapters(ZwxApplicationContext context) {
        for (ZwxHandlerMapping handlerMapping : handlerMappings) {
            handlerAdapters.put(handlerMapping,new ZwxHandlerAdapter());
        }
    }

    private void initHandlerMappings(ZwxApplicationContext context) {
        // 判断IOC是否加载
        if (context.getBeanDefinitionCount()==0){return;}

        String[] beanDefinitionBeanNames = context.getBeanDefinitionBeanNames();
        for (String beanDefinitionBeanName : beanDefinitionBeanNames) {
            // 在容器获取对象
            Object instanceObject = applicationContext.getBean(beanDefinitionBeanName);

            Class<?> instanceObjectClass = instanceObject.getClass();
            if (!instanceObjectClass.isAnnotationPresent(ZwxController.class)){
                continue;
            }
            String baseUrl = "";
            if (instanceObjectClass.isAnnotationPresent(ZwxRequestMapping.class)){
                ZwxRequestMapping requestMapping = instanceObjectClass.getAnnotation(ZwxRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            // 值处理public修饰的方法 
            Method[] methods = instanceObjectClass.getMethods();
            out:for (Method method : methods) {
                if (!method.isAnnotationPresent(ZwxRequestMapping.class)){
                    continue;
                }
                ZwxRequestMapping annotation = method.getAnnotation(ZwxRequestMapping.class);
                // 多个// 替换成一个 把ZwxRequestMapping里面的*修改为符合java正则匹配的语法
                String url = "/" + baseUrl + annotation.value().trim();
                String regx =  url.replaceAll("/+","/");
                // 把解析的映射关系保存起来
                for (ZwxHandlerMapping handlerMapping : handlerMappings) {
                    if (handlerMapping.getPattern().equals(regx)){
                        continue out;
                    }
                }
                handlerMappings.add(new ZwxHandlerMapping(regx,method,instanceObject));
            }

        }
    }

    private void initThemeResolver(ZwxApplicationContext context) {
        System.out.println("初始化模板处理器 jps ftl vm。。。");
    }

    private void initLocaleResolver(ZwxApplicationContext context) {
        System.out.println("初始化本地语言环境。。。");
    }

    private void initMultipartResolver(ZwxApplicationContext context) {
        System.out.println("多文件上传处理。。。");
    }


}
